// 引入ASTElement和ASTModifiers类型，用于类型检查和代码提示
import { ASTElement, ASTModifiers } from 'types/compiler'

/**
 *  生成组件v-model的跨平台代码
 * Cross-platform code generation for component v-model
 */
export function genComponentModel(
  el: ASTElement, // 当前指令绑定的AST元素
  value: string, // v-model表达式的值
  modifiers: ASTModifiers | null // 修饰符对象，可能包含.number、.trim等
): void {
  // 从修饰符对象解构出number和trim属性，默认为undefined
  const { number, trim } = modifiers || {}
 // 基础值表达式，用于后续生成赋值代码
  const baseValueExpression = '$$v'
  // 初始化值表达式为基础值表达式
  let valueExpression = baseValueExpression
  // 如果存在.trim修饰符，则对值进行trim处理
  if (trim) {
    valueExpression =
      `(typeof ${baseValueExpression} === 'string'` +
      `? ${baseValueExpression}.trim()` +
      `: ${baseValueExpression})`
  }
  // 如果存在.number修饰符，则将值转换为数字
  if (number) {
    valueExpression = `_n(${valueExpression})`
  }
    // 生成赋值代码，例如：this.someValue = valueExpression
  const assignment = genAssignmentCode(value, valueExpression)
// 将生成的模型信息保存到el.model上，供后续使用
  el.model = {
    value: `(${value})`, // 原始v-model表达式
    expression: JSON.stringify(value), // 原始v-model表达式的字符串形式
    callback: `function (${baseValueExpression}) {${assignment}}` // 更新值的回调函数
  }
}

/**
 * Cross-platform codegen helper for generating v-model value assignment code.
 * 生成v-model值赋代码的跨平台辅助函数
 */
export function genAssignmentCode(value: string, assignment: string): string {
  // 解析v-model表达式，得到基础路径和最终键段
  const res = parseModel(value)
    // 如果键段为null，表示是直接赋值，如：this.value = ...
  if (res.key === null) {
    return `${value}=${assignment}`
  } else {
     // 否则使用Vue的$set方法赋值，以确保响应式更新
    return `$set(${res.exp}, ${res.key}, ${assignment})`
  }
}

/**
 * Parse a v-model expression into a base path and a final key segment.
 * Handles both dot-path and possible square brackets.
 *
 * Possible cases:
 *
 * - test
 * - test[key]
 * - test[test1[key]]
 * - test["a"][key]
 * - xxx.test[a[a].test1[key]]
 * - test.xxx.a["asa"][test1[key]]
 *
 */

let len, str, chr, index, expressionPos, expressionEndPos

type ModelParseResult = {
  exp: string
  key: string | null
}
// 解析v-model表达式的函数
export function parseModel(val: string): ModelParseResult {
  // Fix https://github.com/vuejs/vue/pull/7730
  // allow v-model="obj.val " (trailing whitespace)
  // 去除首尾空格
  val = val.trim()
  len = val.length
// 如果不包含方括号或方括号不是最后一个字符，则按点路径处理
  if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
    index = val.lastIndexOf('.')
    if (index > -1) {
       // 包含点，分割为基础路径和键段
      return {
        exp: val.slice(0, index), // 基础路径
        key: '"' + val.slice(index + 1) + '"' // 键段，用双引号包裹
      }
    } else {
      // 不包含点，整个为基础路径
      return {
        exp: val,
        key: null
      }
    }
  }
 // 包含方括号的情况，需要更复杂的解析
  str = val
  index = expressionPos = expressionEndPos = 0
// 解析v-model表达式，返回基础路径和键段的结果对象
  while (!eof()) {
    chr = next()
    /* istanbul ignore if */
    if (isStringStart(chr)) {
      parseString(chr)
    } else if (chr === 0x5b) {
      parseBracket(chr)
    }
  }

  return {
    exp: val.slice(0, expressionPos),
    key: val.slice(expressionPos + 1, expressionEndPos)
  }
}
//辅助函数，返回下一个字符的字符码
function next(): number {
  return str.charCodeAt(++index)
}
// 辅助函数，检查是否到达字符串末尾
function eof(): boolean {
  return index >= len
}
//辅助函数，检查字符是否是字符串的起始字符（'或"）
function isStringStart(chr: number): boolean {
  // 0x22是'"'的字符码，0x27是"'"的字符码
  return chr === 0x22 || chr === 0x27
}
// 解析方括号的辅助函数
function parseBracket(chr: number): void {
  let inBracket = 1 // 方括号计数器
  expressionPos = index // 记录表达式开始的位置
  while (!eof()) {
    chr = next()
    if (isStringStart(chr)) {
      parseString(chr)
      continue
    }
    if (chr === 0x5b) inBracket++ // 遇到'['，计数器加1
    if (chr === 0x5d) inBracket-- // 遇到']'，计数器减1
    if (inBracket === 0) {
      expressionEndPos = index // 记录表达式结束的位置
      break
    }
  }
}
// 解析字符串的辅助函数
function parseString(chr: number): void {
  const stringQuote = chr // 记录字符串的引号类型
  while (!eof()) {
    chr = next()
    // 遇到相同的引号，字符串结束
    if (chr === stringQuote) {
      break
    }
  }
}
